package com.bdqn.shiro.config.shiro;


import com.bdqn.shiro.pojo.Right;
import com.bdqn.shiro.pojo.SysRight;
import com.bdqn.shiro.service.SysRightService;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Resource
    private SysRightService sysRightService;

    @Bean
    public MyShiroRealm myShiroRealm() { //自定义 Realm
        MyShiroRealm shiroRealm = new MyShiroRealm();

        //设置凭证匹配器(加密策略)
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return shiroRealm;
    }

    /**
     * 定义Shiro加密策略注册为Spring的Bean
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //使用md5算法进行加密
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //设置散列次数,加密几次
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

    @Bean
    public SecurityManager securityManager() { //安全管理器 SecurityManager
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //注入 Realm
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {//Shiro 过滤器：权限验证
        ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
        //注入 SecurityManager
        shiroFilterFactory.setSecurityManager(securityManager);
        //权限验证：使用 Filter 控制资源(URL)的访问
        shiroFilterFactory.setLoginUrl("/toLogin");
        shiroFilterFactory.setSuccessUrl("/main");
        shiroFilterFactory.setUnauthorizedUrl("/403");

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();//必须使用 LinkedHashMap(有序集合)
        //配置可以匿名访问的资源(URL)：静态资源
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/localcss/**", "anon");
        filterChainDefinitionMap.put("/localjs/**", "anon");

        filterChainDefinitionMap.put("/doLogin", "anon");
        filterChainDefinitionMap.put("/logout", "logout");//注销过滤器，自动注销
        //动态授权
        List<SysRight> rights = sysRightService.list();
        for (SysRight right : rights) {
            if (right.getRightUrl() != null && !right.getRightUrl().trim().equals("")) {
                filterChainDefinitionMap.put(right.getRightUrl(), "perms[" + right.getRightCode() + "]");
            }
        }
        //配置认证访问：其他资源(URL)必须认证通过才能访问
        filterChainDefinitionMap.put("/**", "authc"); //必须放在过滤器链的最后面

        shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactory;
    }

    /**
     * 2. * 开启 Shiro 注解(如@RequiresRoles,@RequiresPermissions),
     * 3. * 需借助 SpringAOP 扫描使用 Shiro 注解的类,并在必要时进行安全逻辑验证
     * 4. * 配置以下两个 bean(DefaultAdvisorAutoProxyCreator 和
     * AuthorizationAttributeSourceAdvisor)
     * 5.
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启 aop 注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
